package gofire

import (
	"fmt"
	"net/http"
	"sync"
)

type RouteRegistrar interface {
	Get(url string, handleFunc HandleFunc)
	Post(url string, handleFunc HandleFunc)
	Put(url string, handleFunc HandleFunc)
	Delete(url string, handleFunc HandleFunc)
}

type RouteKey struct {
	Method string
	URL    string
}

type Router struct {
	sync.Mutex
	m map[RouteKey]HandleFunc
	s *HttpServer
}

func newRouter() *Router {
	return &Router{
		Mutex: sync.Mutex{},
		m:     make(map[RouteKey]HandleFunc),
	}
}

func (r *Router) bindServer(server *HttpServer) {
	r.s = server
}

func (r *Router) add(method, url string, handleFunc HandleFunc) {
	r.Lock()
	defer r.Unlock()

	key := RouteKey{
		Method: method,
		URL:    url,
	}

	if _, exists := r.m[key]; exists {
		panic(fmt.Sprintf("duplicate route: %s %s", method, url))
	}

	r.m[key] = r.s.shutdown.ServeIfNotClosed(handleFunc)
}

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	r.Lock()
	defer r.Unlock()
	method, url := req.Method, req.URL.Path
	key := RouteKey{
		Method: method,
		URL:    url,
	}
	handler, ok := r.m[key]
	if !ok {
		http.Error(w, "Not found", http.StatusNotFound)
		return
	}
	context := &Context{
		Request: req,
		w:       w,
		server:  r.s,
	}
	err := handler(context)
	if err != nil {
		context.ServerError(err)
	}
}

type RouteRegistry struct {
	router *Router
}

func (r *RouteRegistry) Get(url string, handleFunc HandleFunc) {
	r.router.add(http.MethodGet, url, handleFunc)
}

func (r *RouteRegistry) Post(url string, handleFunc HandleFunc) {
	r.router.add(http.MethodPost, url, handleFunc)
}

func (r *RouteRegistry) Put(url string, handleFunc HandleFunc) {
	r.router.add(http.MethodPut, url, handleFunc)
}

func (r *RouteRegistry) Delete(url string, handleFunc HandleFunc) {
	r.router.add(http.MethodDelete, url, handleFunc)
}

func (r *RouteRegistry) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	r.router.ServeHTTP(w, req)
}
